1、驱动中的中断处理
	在内核中可以通过 request_irq 或 request_threaded_irq 来注册某个中断的中断处理函数，通过 free_irq 来注销
request_irq 或 request_threaded_irq 注册的中断处理函数
2、中断处理函数原形
	/**
	 * irq 中断号
	 * dev 注册时传递的参数
	 * 返回 IRQ_NONE 表示不是此设备产生的中断， IRQ_HANDLED 中断被正常处理， IRQ_WAKE_THREAD 唤醒中断处理线
	 * 程，适用于常用 request_threaded_irq 注册的中断处理函数
	 */
	irqreturn_t (*irq_handler)(int irq, void *dev)
3、注册中断处理函数
	/**
	 * irq 中断号
	 * handler 中断处理函数，即中断上半部，为 NULL 则使用默认的中断处理函数
	 * thread_fn 中断线程化函数，中断处理函数返回 IRQ_WAKE_THREAD 会执行此函数，为 NULL 则不创建中断处理线程
	 * flags 标志，IRQF_SHARED 共享中断
	 *             IRQF_ONESHOT 中断线程化函数结束前不再接收此中断
	 *             IRQF_TRIGGER_NONE 无触发
	 *             IRQF_TRIGGER_RISING 上升沿触发
	 *             IRQF_TRIGGER_FALLING下降沿触发
	 *             IRQF_TRIGGER_HIGH 高电平触发
	 *             IRQF_TRIGGER_LOW 低电平触发
	 * name 中断名字，设置以后可以在/proc/interrupts 文件中看到对应的中断名字
	 * dev 如果将 flags 设置为 IRQF_SHARED 的话，dev 用来区分不同的中断
	 */
	int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void 
*dev)
	int request_threaded_irq(unsigned int irq, irq_handler_t handler,  irq_handler_t thread_fn, unsigned 
long flags, const char *name, void *dev);
4、注销中断处理函数
	/**
	 * irq 中断号
	 * dev 如果中断设置为共享中断，此参数用来区分具体的中断，必须与注册时传递的参数一致。共享中断只有在最后一
	 * 次释放中断处理函数的时候才会被禁止掉
	 */
	void free_irq(unsigned int irq, void *dev)
5、中断控制
	//禁止本核中断，并保存当前中断使能状态
	local_irq_save(flags)
	//恢复本核中断状态
	local_irq_restore(flags)
	//使能指定中断
	enable_irq(unsigned int irq)
	//禁止指定中断，需要等待中断处理函数执行完成
	disable_irq(unsigned int irq)
	//立即禁止指定中断
	disable_irq_nosync(unsigned int irq)
6、编写中断函数的注意事项
	1）因为不存在进程上下文，所以不能调用可能会引起休眠的函数
	2）因为linux默认不支持中断嵌套，所以中断处理时间要尽可能的短
7、中断延迟处理
	在中断处理函数中只能完成一些紧急或耗时极短的操作，对于不紧急且耗时较长的操作需要放到中断处理函数结束后进
行处理，如采用 tasklet 、工作队列中、中断线程化等方法
8、tasklet
	tasklet 是一种中断延迟手段，在中断程序中可以调度 tasklet ，当中断程序结束后相应的 tasklet 回调函数将会被
执行， tasklet 是利用软中断实现的，它处于软中断上下文（可能处于进程上下文，也可能处于中断上下文），所以不能
调用可能会引起休眠的函数，但是对执行时间的要求相对宽松些，如下是tasklet的常用函数：
	/**
	 * 静态定义并初始化一个tasklet
	 * name 变量名
	 * func 回调函数
	 * data 回调函数参数
	 */
	DECLARE_TASKLET(name, func, data)
	/**
	 * 初始化tasklet
	 * t 要初始化的 tasklet
	 * func 回调函数
	 * data 回调函数参数
	 */
	void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
	/**
	 * 调度tasklet
	 * t 要调度的 tasklet
	 */
	void tasklet_schedule(struct tasklet_struct *t)
9、工作队列
	工作队列与 tasklet 类似，但是工作队列是在进程上下文执行，可以调用可能会引起休眠的函数,如下是工作队列的常
用函数：
	/**
	 * 静态定义并初始化一个工作队列
	 * n 变量名
	 * f 工作队列回调函数
	 */
	DECLARE_WORK(n, f)
	/**
	 * 初始化工作队列
	 * work 要初始化的工作队列
	 * _func 工作队列回调函数
	 */
	INIT_WORK(_work, _func)
	/**
	 * 调度工作队列
	 * work 要调度的工作队列
	 */
	bool schedule_work(struct work_struct *work)
10、设备树里使用中断
	interrupt-controller 一个空属性，表示此节点是一个中断控制器。
	#interrupt-cells 类似 #address-cells 和 #size-cells ，表示引用此中断控制器时需要传递多少个参数。
	interrupt-parent 设置节点所使用的中断控制器，子节点默认使用父节点的中断控制器。
	interrupts 描述节点的中断信息，包括中断类型、中断号、触发方式等，具体含义与中断控制器相关。
	interrupts-extended 描述节点的中断信息和所使用的中断控制器
	使用GIC中断控制器：
	//设置此节点所使用的中断控制器
	interrupt-parent = <&intc>;
	/*
	* 描述中断信息，依次是：
	*     中断类型（GIC_SPI或GIC_PPI）
	*     中断物理编号
	*     触发方式，其中bit[3:0]表示中断触发类型，bit[15:8]为 PPI 中断的 CPU 掩码
	*/
	interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>,
				<GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>;
	使用GPIO中断控制器：
	//设置此节点所使用的中断控制器
	interrupt-parent = <&gpiog>;
	//描述中断信息，分别是GPIO号、触发方式
	interrupts = <1 IRQ_TYPE_EDGE_FALLING>;
	通过设备树节点获取逻辑中断号（实际上是根据设备树描述分配一个逻辑中断号，它通过 irq_create_of_mapping 函
数实现）：
	unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
	//相对于irq_of_parse_and_map多了错误检测
	int of_irq_get(struct device_node *dev, int index)
	通过gpio号获取中断号：
	int gpio_to_irq(unsigned int gpio)
	获取中断触发类型
	u32 irq_get_trigger_type(unsigned int irq)
	对于大多数总线设备内核都会解析设备树中描述的中断信息，在驱动中可以获得相应的中断号，如 iic 设备可以通过
i2c_client 的 irq 成员获取中断， spi 设备可以通过 spi_device 的 irq 成员获取中断（平台设备在匹配过程中不会解
析设备树中的中断信息，但是可以通过 platform_get_irq 或 platform_get_irq_byname 获取中断）